/* File: ConcreteGene.java
 * Purpose: A set of traits that an organism has
 * Author: Team Karp
 * Date: 12/13/07
 * Revisions: 12/13/07, Adam Patterson - Initial Version
 *              [2008-01-13, Adam Patterson - Implement attacking damage]
 * Additional Requirements: 
 */
package protovitae.data;

import java.util.ArrayList;
import java.util.HashMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * @version 1.0
 * @created 06-Dec-2007 16:19:09
 */
public class ConcreteGene extends Gene implements XMLSavable {
    private int dominance;  //manages the order of application of genes
    private HashMap<String, String> exposedOverrides; // Javascript code used to initialize the organism's exposed attributes.
    private int id; // The ide of the gene, necessary.
    private GeneActionStrategy move = new MoveStrategy();
    private GeneActionStrategy attack = new AttackStrategy();
    private GeneActionStrategy eat = new EatStrategy();
    private GeneActionStrategy dance = new DanceStrategy();
    private GeneActionStrategy mate = new MateStrategy();
    private GeneActionStrategy act = new ActStrategy();
    //private Color color;    //This is the color of the organism, it affects food types (for both eathing and being eaten)
    //This isn't being used? Why not call getColor() on parent?
    private ConcreteOrganism parent;
    private ArrayList<Byte> bytes = new ArrayList<Byte>();
    
    /**
     * Returns a string that represents this gene.
     * @return A string that represents this gene.
     */
    public String getGeneticInformation() {
        StringBuilder builder = new StringBuilder();
        builder.append(",");
        builder.append(id);
        builder.append("#");
        for (byte b: move.getGeneticInformation()) {
            builder.append(b);
        }
        for (byte b: attack.getGeneticInformation()) {
            builder.append(b);
        }
        for (byte b: eat.getGeneticInformation()) {
            builder.append(b);
        }
        for (byte b: dance.getGeneticInformation()) {
            builder.append(b);
        }
        for (byte b: mate.getGeneticInformation()) {
            builder.append(b);
        }
        for (byte b: act.getGeneticInformation()) {
            builder.append(b);
        }
        return this.name;
        //return builder.toString();
    }

    /**
     * 
     * @param organism
     * @param attribute
     */
    public ConcreteGene(ConcreteOrganism organism) {
        parent = organism;
        //saveStrategy = new XMLSaveGene(this);
        exposedOverrides = new HashMap();
    }
    
    /** Copy constructor. Used by the organism editor **/
    public ConcreteGene(ConcreteGene orig, ConcreteOrganism organism) {
        parent = organism;
        //saveStrategy = new XMLSaveGene(this);
        exposedOverrides = orig.getExposedOverrides();
        this.name = orig.name;
        
        dominance = orig.dominance;  //manages the order of application of genes
        move = orig.move;
        attack = orig.attack;
        eat = orig.eat;
        dance = orig.dance;
        mate = orig.mate;
        act = orig.act;
        //color = organism.getColor(); //Unused variable
    }

    public Byte getGeneticVariable(int index){
        if(index>=bytes.size()){
            return null;
        }
      
        return bytes.get(index);
    }
    
    /**
     * 
     * @param organism
     */
    public ArrayList act(Simulation environment, Organism organism) {
        ArrayList votedInfo = (ArrayList) act.execute(environment, parent);

        return votedInfo;
    }

    /**
     * 
     * @param organism the organism being attacked
     */
    public void attack(Organism organism) {
        int damage = ((Integer) attack.execute(organism, parent)).intValue();
        //apply damage
        ((ConcreteOrganism)organism).setAttributeValue(Organism.ATTRIBUTE_TYPE.HEALTH,
                ((ConcreteOrganism)organism).getAttribute(Organism.ATTRIBUTE_TYPE.HEALTH).getCurrent() - damage);

    }

    /**
     * 
     * @param organism
     */
    @Override
    public void dance(Simulation parent, Organism organism) {
        dance.execute(parent, (ConcreteOrganism)organism);
    }

    /**
     * 
     * @param food the dead organism being devoured
     */
    @Override
    public void eat(Organism food) {
        eat.execute(food, parent);
    }

    /** This determines which gene is applied first when applied to a creature. 
     * @return the order of appliance. A lower number means it's applied earlier
     */
    @Override
    public int getDominance() {
        return dominance;
    }

    /**
     * Execute the mating functionality
     * @param organism The organism to attempt to mate with
     */
    @Override
    public void mate(Organism organism) {
        mate.execute(organism, parent);
    }

    /**
     * Execute movement functionality
     * @param environment The enviornment the organism is moving inside of
     */
    @Override
    public Position move(Simulation environment, ConcreteOrganism organism) {
        return (Position) move.execute(environment, organism);
    }

    /**
     * 
     * @param parent
     */
    @Override
    public void save(Element parentElm, Document doc) {
        Element gene = doc.createElement("gene");
        
        Element savid = doc.createElement("id");
        savid.appendChild(doc.createTextNode(""+id));
        gene.appendChild(savid);
        
        Element xmlname = doc.createElement("name");
        xmlname.appendChild(doc.createTextNode(name));
        gene.appendChild(xmlname);
        
        Element savdom = doc.createElement("dominance");
        savdom.appendChild(doc.createTextNode(""+dominance));
        gene.appendChild(savdom);
        
        //Exposed overrides aren't used yet.
        //Bytes array doesn't appear to need saving. We'll see...
        
        act.save(gene, doc);
        attack.save(gene, doc);
        dance.save(gene, doc);
        eat.save(gene, doc);
        mate.save(gene, doc);
        move.save(gene, doc);
        parentElm.appendChild(gene);
    }

    @Override
    public void load(Element input) {
        try{
            NodeList loadid = input.getElementsByTagName("id");
            NodeList loadname = input.getElementsByTagName("name");
            NodeList loaddom = input.getElementsByTagName("dominance");
            id = Integer.parseInt(loadid.item(0).getTextContent());
            name = loadname.item(0).getTextContent();
            dominance = Integer.parseInt(loaddom.item(0).getTextContent());
            
            act.load(input);
            attack.load(input);
            dance.load(input);
            eat.load(input);
            mate.load(input);
            move.load(input);
        }
        catch(Exception e){
            e.printStackTrace(); //A bug occured
        }
    }
    
    /**
     * Converts the gene into a string that can be used in GAs.
     * Comment: We populate list boxes with genes. The list box uses toString as the text to display in that list box.
     * What about something like getGeneString for the functionality you describe above?
     */
    @Override
    public String toString() {
        return super.toString();
    }

    /** Get the eat strategy of this gene.
     * @return This gene's eat strategy.
     */
    public GeneActionStrategy getEatStrategy() {
        return eat;
    }

    //To do: Comment the rest of these
    public GeneActionStrategy getMateStrategy() {
        return mate;
    }

    public GeneActionStrategy getMoveStrategy() {
        return move;
    }

    public GeneActionStrategy getAttackStrategy() {
        return attack;
    }

    public GeneActionStrategy getDanceStrategy() {
        return dance;
    }

    public GeneActionStrategy getActStrategy() {
        return act;
    }

    /** Get the exposed attribute overrides hash map. **/
    public HashMap<String, String> getExposedOverrides() {
        return exposedOverrides;
    }
}
